<?php
/*
*  Класс обеспечивает работу с датами. Преобразование из формата, понятного человеку в формат БД и обратно
*/

/**
 * Класс для работы с датой и временем
 *
 *Чаще всего используются 2 функции:
 * getTimeForUser возвращает дату и время во временном поясе пользователя, нужно передавать время из бд
 * gLocalTime2BD возвращает настоящее время по гринфичу, для записи в бд
 */
class Service_Date {

	public static $timestampOneDayInterval = 86400;
	/**
	 * Возвращает время по гринфичу, для записи в бд
	 *
	 * @param mixed $localstamp
	 * @return время по гринфичу
	 */
	public static function gLocalTime2BD($localstamp = false) {
		if ($localstamp === false) $localstamp = time();
		return gmdate("Y-m-d H:i:s", $localstamp);
	}

	/**
	 * Возвращает время по гринфичу, для записи в бд
	 *
	 * @param mixed $localstamp
	 * @return время по гринфичу
	 */
	protected static function date2BD($addTime = 0) {
		$localstamp = time() + $addTime;
		return gmdate("Y-m-d", $localstamp);
	}
	
	public static function dateToBD($date){
		$arrDate = explode('/', $date);
		if (sizeof($arrDate) != 3) return '';
		return $arrDate[2] . '-' . $arrDate[1] . '-' . $arrDate[0];
	}

	public static function dateBDToHuman($date = null, $addTime = 0){
		if (empty($date)) $date = self::date2BD($addTime);
		$arrDate = explode('-', $date);
		if (sizeof($arrDate) != 3) return '';
		return $arrDate[2] . '/' . $arrDate[1] . '/' . $arrDate[0];
	}
	
	public static function dateToRus($date){
		$arrDate = explode('/', $date);
		if (sizeof($arrDate) != 3) return '';
		return intval($arrDate[0]) . ' ' . self::getMonth($arrDate[1]) . ' ' . $arrDate[2];
	}	
	
	public static function getMonth($month, $fullName = false, $case = 1) {
		Service_Debug::setDebug();
		$language = Service_Translate::getLang();
		if ($language == 'ru') $arrMothes = array('Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь');
		else $arrMothes = array('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December');
		if ($fullName) $strMonth = $arrMothes[$month - 1];
		else $strMonth = Service_Function::utf8_substr($arrMothes[$month - 1], 0, 3);
		if ($language == 'ru' && $case == 2 && $fullName) {
			$lastSymbol = Service_Function::utf8_substr($strMonth, - 1);
			if ($lastSymbol == 'ь' || $lastSymbol == 'й') $strMonth = Service_Function::utf8_substr($strMonth, 0, - 1) . 'я';
			else $strMonth .= 'а';	
		} 
		return $strMonth;
	}
	
	public static function getDayOfWeek($date) {
		Service_Debug::setDebug();
		$arrDate = explode('/', $date);
		if (sizeof($arrDate) != 3) return '';
		$date = mktime(0, 0, 0, $arrDate[1], $arrDate[0], $arrDate[2]);
		$arrDate = getdate($date);
		$weekDay = $arrDate['wday'];
		return self::getWeekDay($weekDay);
		///return intval($arrDate[0]) . ' ' . self::getMonth($arrDate[1]) . ' ' . $arrDate[2];		
	}
	
	public static function getWeekDay($weekDay) {
		Service_Debug::setDebug();
		$language = Service_Translate::getLang();
		switch ($weekDay) {
			case 1: return ($language == 'ru' ? 'Пн' : 'Mon');	break;
			case 2: return ($language == 'ru' ? 'Вт' : 'Tue');	break;
			case 3: return ($language == 'ru' ? 'Ср' : 'Wed');	break;
			case 4: return ($language == 'ru' ? 'Чт' : 'Thu');	break;
			case 5: return ($language == 'ru' ? 'Пт' : 'Fri');	break;
			case 6: return ($language == 'ru' ? 'Сб' : 'Sat');	break;
			case 0: return ($language == 'ru' ? 'Вс' : 'Sun');	break;
		}
	}	
	
	/**
	 * Получает timestamp
	 * 
	 * @param string $dbFormattedDate
	 */
	public static function getTimestamp($dbFormattedDate) {
		Service_Debug::setDebug();
		list($year, $month, $day) = explode('-', $dbFormattedDate);
		return mktime(0, 0, 0, (int) $month, (int) $day, (int) $year);
	}
	
	/**
	 * Разница между двумя датами
	 * 
	 * $interval can be:
   * yyyy - Number of full years
   * q - Number of full quarters
   * m - Number of full months
   * y - Difference between day numbers
   *     (eg 1st Jan 2004 is "1", the first day. 2nd Feb 2003 is "33". The datediff is "-32".)
   * d - Number of full days
   * w - Number of full weekdays
   * ww - Number of full weeks
   * h - Number of full hours
   * n - Number of full minutes
   * s - Number of full seconds (default)
   */
	public static function dateDiff($datefrom, $dateto, $interval, $using_timestamps = false) {
    if (!$using_timestamps) {
        $datefrom = strtotime($datefrom, 0);
        $dateto = strtotime($dateto, 0);
    }
    $difference = $dateto - $datefrom; // Difference in seconds
     
    switch($interval) {
     
    case 'yyyy': // Number of full years

        $years_difference = floor($difference / 31536000);
        if (mktime(date("H", $datefrom), date("i", $datefrom), date("s", $datefrom), date("n", $datefrom), date("j", $datefrom), date("Y", $datefrom)+$years_difference) > $dateto) {
            $years_difference--;
        }
        if (mktime(date("H", $dateto), date("i", $dateto), date("s", $dateto), date("n", $dateto), date("j", $dateto), date("Y", $dateto)-($years_difference+1)) > $datefrom) {
            $years_difference++;
        }
        $datediff = $years_difference;
        break;

    case "q": // Number of full quarters

        $quarters_difference = floor($difference / 8035200);
        $months_difference = 0;
        while (mktime(date("H", $datefrom), date("i", $datefrom), date("s", $datefrom), date("n", $datefrom)+($quarters_difference*3), date("j", $dateto), date("Y", $datefrom)) < $dateto) {
            $months_difference++;
        }
        $quarters_difference--;
        $datediff = $quarters_difference;
        break;

    case "m": // Number of full months

        $months_difference = floor($difference / 2678400);
        while (mktime(date("H", $datefrom), date("i", $datefrom), date("s", $datefrom), date("n", $datefrom)+($months_difference), date("j", $dateto), date("Y", $datefrom)) < $dateto) {
            $months_difference++;
        }
        $months_difference--;
        $datediff = $months_difference;
        break;

    case 'y': // Difference between day numbers

        $datediff = date("z", $dateto) - date("z", $datefrom);
        break;

    case "d": // Number of full days

        $datediff = floor($difference / 86400);
        break;

    case "w": // Number of full weekdays

        $days_difference = floor($difference / 86400);
        $weeks_difference = floor($days_difference / 7); // Complete weeks
        $first_day = date("w", $datefrom);
        $days_remainder = floor($days_difference % 7);
        $odd_days = $first_day + $days_remainder; // Do we have a Saturday or Sunday in the remainder?
        if ($odd_days > 7) { // Sunday
            $days_remainder--;
        }
        if ($odd_days > 6) { // Saturday
            $days_remainder--;
        }
        $datediff = ($weeks_difference * 5) + $days_remainder;
        break;
    case "ww": // Number of full weeks
        $datediff = floor($difference / 604800);
        break;
    case "h": // Number of full hours
        $datediff = floor($difference / 3600);
        break;
    case "n": // Number of full minutes
        $datediff = floor($difference / 60);
        break;
    default: // Number of full seconds (default)
        $datediff = $difference;
        break;
    }    
    return $datediff;
	}
	
	/**
	 * Изменят формат даты
	 * 
	 * @param string $theDate - дата в формате $mask 
	 * @param string $mask - входная маска
	 * @param string $outputMask - выходная маска
	 */
	public static function changeDateFormat($theDate, $mask, $outputMask = 'U') {
		$maskOrig = $mask;
		if ($mask == 'U') $stamp = $theDate;
		$obj['seconds'] = null;
		$obj['minutes'] = null;
		$obj['hours'] = null;
		$obj['days'] = null;
		$obj['months'] = null;
		$obj['years'] = null;
		$obj['ampm'] = null;
		
		// define the valid values that we will use to check
		// value => length
		$all = array (

		//time
		's' => 'ss', // Seconds, with leading zeros
		'i' => 'ii', // Minutes with leading zeros
		'H' => 'HH', // 24-hour format of an hour with leading zeros
		'h' => 'hh', // 12-hour format of an hour with leading zeros
		'G' => 'GG', // 24-hour format of an hour without leading zeros
		'g' => 'gg', // 12-hour format of an hour without leading zeros
		'A' => 'AA', // Uppercase Ante meridiem and Post meridiem
		'a' => 'aa', // Lowercase Ante meridiem and Post meridiem

		//year
		'y' => 'yy', // A full numeric representation of a year, 4 digits
		'Y' => 'YYYY', // A two digit representation of a year

		//month
		'm' => 'mm', // A numeric representation of a month with leading zeros.
		'M' => 'MMM', // A textual representation of a month.  3 letters.  ex: Jan, Feb, Mar, Apr...
		'n' => 'nn', // Numeric representation of a month, without leading zeros

		//days
		'd' => 'dd', // Day of the month, 2 digits with leading zeros
		'j' => 'jj', // Day of the month without leading zeros
		'S' => 'SS', // English ordinal suffix for the day of the month, 2 characters (st, nd, rd, or th. works well with j)
		'D' => 'DDD' );// Textual representation of day of the week (Sun, Mon, Tue, Wed)
		
		// this will give us a mask with full length fields
		$mask = str_replace(array_keys($all), $all, $mask);
		$vals = array ();
		//loop through each character of $mask starting at the beginning
		for($i = 0; $i < strlen($maskOrig); $i++) {
			//get the current character
			$thisChar = substr($maskOrig, $i, 1);
			//if the character is not in the $all array, skip it
			if (array_key_exists($thisChar, $all)) {
				$type = $thisChar;
				$chars = $all[$type];
				// get position of the current char
				if (($pos = strpos($mask, $chars)) === false) continue;
				// find the value from $theDate
				$val = substr($theDate, $pos, strlen($chars));
				/*		START FIX FOR UNITS WITHOUT LEADING ZEROS		*/
				if ($type == "n" || $type == "j" || $type == "g" || $type == "G") {
					//if its not numeric, try a shorter digit
					if (!is_numeric($val)) {
						$val = substr($theDate, $pos, strlen($chars) - 1);
						$mask = str_replace($chars, $type, $mask);
					}
					else {
						//try numeric value checking
						switch ($type) {
							case "n":
								if ($val > 12 || $val < 1) { //month must be between 1-12
									$val = substr($theDate, $pos, strlen($chars) - 1);
									$mask = str_replace($chars, $type, $mask);
								}
								break;
							case "j":
								if ($val > 31 || $val < 1) { //day must be between 1-31
									$val = substr($theDate, $pos, strlen($chars) - 1);
									$mask = str_replace($chars, $type, $mask);
								}
								break;
							case "g":
								if ($val > 12 || $val < 1) { //day must be between 1-12
									$val = substr($theDate, $pos, strlen($chars) - 1);
									$mask = str_replace($chars, $type, $mask);
								}
								break;
							case "G":
								if ($val > 24 || $val < 1) { //day must be between 1-24
									$val = substr($theDate, $pos, strlen($chars) - 1);
									$mask = str_replace($chars, $type, $mask);
								}
								break;
						}
					}
				}
				/*		END FIX FOR UNITS WITHOUT LEADING ZEROS		*/
				//save this value
				$vals [$type] = $val;
			}
		}
		foreach ( $vals as $type => $val ) {
			switch ($type) {
				case 's': $obj['seconds'] = $val; break;
				case 'i': $obj['minutes'] = $val; break;
				case 'H': case 'h': $obj['hours'] = $val; break;
				case 'A':	case 'a':	$obj['ampm'] = $val; break;
				case 'y': $obj['years'] = (($val < 30) ? '20' : '19') . $val;	break;
				case 'Y':	$obj['years'] = $val;	break;
				case 'm':	$obj['months'] = $val; break;
				case 'M':	$obj['months'] = self::monthNum($val, true); break;
				case 'R':	$obj['months'] = self::monthNum($val, true); break;
				case 'd':	$obj['days'] = $val; break;
				//ones without leading zeros:
				case 'n':	$obj['months'] = $val; break;
				case 'j':	$obj['days'] = $val; break;
				case 'g': $obj['hours'] = $val;	break;
				case 'G':	$obj['hours'] = $val;	break;
			}
		}
			//if its pm, add 12 hours
		if (strtolower($obj['ampm']) == "pm") $obj['hours'] = $obj['hours'] + 12;
		if (empty($stamp)) $stamp = mktime(( int ) $obj['hours'], ( int ) $obj['minutes'], ( int ) $obj['seconds'], ( int ) $obj['months'], ( int ) $obj['days'], ( int ) $obj['years']);
		$arrOutputMask = str_split($outputMask);
		if (in_array('R', $arrOutputMask)) { // Если есть русский длинный месяц
			$outputMask = str_replace('R', self::getMonth($obj['months'], true, 2), $outputMask);
		}
		if (in_array('r', $arrOutputMask)) { // Если есть русский короткий месяц
			$outputMask = str_replace('r', self::getMonth($obj['months'], false, 2), $outputMask);
		}
		return date($outputMask, $stamp);
	}
	
	public static function monthNum($theMonth, $twoDigit=false) {
		switch (strtolower($theMonth)) {
			case 'jan':
			case 'jan.';
			case 'january':
				return ($twoDigit == true ? '01': '1');
				break;
			case 'feb':
			case 'feb.':
			case 'february':
			case 'febuary':
				return ($twoDigit == true ? '02': '2');
				break;
			case 'mar':
			case 'mar.':
			case 'march':
				return ($twoDigit == true ? '03': '3');
				break;
			case 'apr':
			case 'apr.':
			case 'april':
				return ($twoDigit == true ? '04': '4');
				break;
			case 'may':
			case 'may.':
				return ($twoDigit == true ? '05': '5');
				break;
			case 'jun':
			case 'jun.':
			case 'june':
				return ($twoDigit == true ? '06': '6');
				break;
			case 'jul':
			case 'jul.':
			case 'july':
				return ($twoDigit == true ? '07': '7');
				break;
			case 'aug':
			case 'aug.':
			case 'august':
				return ($twoDigit == true ? '08': '8');
				break;
			case 'sep':
			case 'sep.':
			case 'sept':
			case 'sept.':
			case 'september':
				return ($twoDigit == true ? '09': '9');
				break;
			case 'oct':
			case 'oct.':
			case 'october':
				return '10';
				break;
			case 'nov':
			case 'nov.':
			case 'november':
				return '11';
				break;
			case 'dec':
			case 'dec.':
			case 'december':
				return '12';
				break;
			default:
				return false;
				break;
		}
	}

	public static function getRusDate($date, $fullNames = true) {
		Service_Debug::setDebug();
		if (Service_Translate::getLang() == 'ru') {
			if ($fullNames) return self::changeDateFormat($date, 'd/m/Y', 'd R');
			else return self::changeDateFormat($date, 'd/m/Y', 'd r');
		}
		else {
			if ($fullNames) return self::changeDateFormat($date, 'd/m/Y', 'd F');
			else return self::changeDateFormat($date, 'd/m/Y', 'd M');
		}
		
	}


	// Пример обработки дат для PHP 5.3
  //$objDateTime = DateTime::createFromFormat($format , $strDate);
  //echo date_format($objDateTime, $format2);
}
?>